package com.xinyue.game.gateway.local;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.xinyue.game.gateway.common.GameGatewayConfig;
import com.xinyue.game.utils.GameServerUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用户连接管理
 *
 * @author 王广帅
 * @since 2023/6/27 21:05
 **/
@Service
public class UserChannelService {

    private final static AttributeKey<String> CHANNEL_BIND_PLAYER_ID_KEY = AttributeKey.valueOf("channelBindPlayerIdKey");

    private Logger logger = LoggerFactory.getLogger(UserChannelService.class);
    /**
     * 缓存用户id与连接上下文的绑定关系
     */
    private Cache<String, ChannelHandlerContext> playerChannelCache;
    /**
     * 保存channelId与ctx的关系
     */
    private Map<String, ChannelHandlerContext> channelHandlerContextMap = new ConcurrentHashMap<>();

    @Autowired
    private GameGatewayConfig gameGatewayConfig;

    @PostConstruct
    public void init() {
        playerChannelCache = CacheBuilder.newBuilder().expireAfterAccess(Duration.ofSeconds(gameGatewayConfig.getMaxChannelCacheModelIdleTime()))
                .initialCapacity(1000).maximumSize(gameGatewayConfig.getMaxChannelCacheModelCount()).removalListener((RemovalListener<String, ChannelHandlerContext>) notification -> {
                    ChannelHandlerContext ctx = notification.getValue();
                    logger.info("连接被从缓存中移除，playerId:{}，channelId: {}", notification.getKey(), GameServerUtil.getChannelId(ctx.channel()));
                    ctx.close();
                }).build();
    }

    public void addChannelCache(String playerId, ChannelHandlerContext ctx) {
        Attribute<String> playerAttr = ctx.channel().attr(CHANNEL_BIND_PLAYER_ID_KEY);
        playerAttr.set(playerId);
        this.playerChannelCache.put(playerId, ctx);
        ctx.channel().closeFuture().addListener(future -> {
            logger.debug("连接断开，移除缓存，playerId: {}, channelId: {}", playerId, GameServerUtil.getChannelId(ctx.channel()));
            playerChannelCache.invalidate(playerId);
        });
    }

    public void addChannelContext(ChannelHandlerContext ctx) {
        String channelId = GameServerUtil.getChannelId(ctx.channel());
        this.channelHandlerContextMap.put(channelId, ctx);
        ctx.channel().closeFuture().addListener(future -> {
            this.channelHandlerContextMap.remove(channelId);
            logger.debug("连接断开，清除ctx缓存,channelId: {}", channelId);
        });
    }

    public ChannelHandlerContext getCtx(String playerId) {
        ChannelHandlerContext ctx = this.playerChannelCache.getIfPresent(playerId);
        if (ctx == null) {
            // 如果根据playerId查询为空时，有可能playerId就是channelId，再查一回
            ctx = this.channelHandlerContextMap.get(playerId);
        }
        return ctx;
    }

    public String getPlayerId(ChannelHandlerContext ctx) {
        Attribute<String> playerAttr = ctx.channel().attr(CHANNEL_BIND_PLAYER_ID_KEY);
        if (playerAttr != null) {
            return playerAttr.get();
        }
        return null;
    }
}
